#ifndef X36_H
#define X36_H

#include "common.h"

#include <vector>

// List of strings, such as `"OBJECT_INSTANCE"`, `"VIASTRUCTUREID"`,
// `"0x00072768"`, or `"DIFF_IMPEDANCE_RULE"`. No obvious pattern?
//
// Variety of other undecoded fields follow, too.
template <AllegroVersion version>
struct x36_x02
{
    uint8_t str[32];
    uint32_t xs[14];

    uint32_t TAIL;
};
#if __cplusplus >= 202002L
template <AllegroVersion version> requires(version >= A_164)
struct x36_x02<version>
{
    uint8_t str[32];
    uint32_t xs[14];
    uint32_t ys[3];
    COND_FIELD(version >= A_172, uint32_t[2], zs);

    uint32_t TAIL;
};
#else
template <> struct x36_x02<A_164>
{
    uint8_t str[32];
    uint32_t xs[14];
    uint32_t ys[3];

    uint32_t TAIL;
};
template <> struct x36_x02<A_165> : x36_x02<A_164> {};
template <> struct x36_x02<A_166> : x36_x02<A_164> {};

template <> struct x36_x02<A_172>
{
    uint8_t str[32];
    uint32_t xs[14];
    uint32_t ys[3];
    uint32_t zs[2];

    uint32_t TAIL;
};
template <> struct x36_x02<A_174> : x36_x02<A_172> {};
#endif

// List of strings, such as `"NO_TYPE"`, `"DEFAULT"`, `"16MIL"`, or
// `"ETH_DIFF"`. No obvious pattern?
template <AllegroVersion version>
struct x36_x03
{
    uint8_t str_16x[32];

    uint32_t TAIL;
};

#if __cplusplus >= 202002L
template <AllegroVersion version> requires(version >= A_172)
struct x36_x03<version>
{
    uint8_t str[64];
    COND_FIELD(version >= A_174, uint32_t, un2);

    uint32_t TAIL;
};
#else
template<> struct x36_x03<A_172>
{
    uint8_t str[64];

    uint32_t TAIL;
};

template<> struct x36_x03<A_174>
{
    uint8_t str[64];
    uint32_t un2;

    uint32_t TAIL;
};
#endif

#if __cplusplus >= 202002L
template <AllegroVersion version>
struct x36_x06
{
    uint16_t n;
    uint8_t r;
    uint8_t s;
    uint32_t un1;
    COND_FIELD(version < A_172, uint32_t[50], un2);
    uint32_t TAIL;
};
#else
template <AllegroVersion version>
struct x36_x06
{
    uint16_t n;
    uint8_t r;
    uint8_t s;
    uint32_t un1;
    uint32_t un2[50];
    uint32_t TAIL;
};

template <> struct x36_x06<A_172>
{
    uint16_t n;
    uint8_t r;
    uint8_t s;
    uint32_t un1;
    uint32_t TAIL;
};
template <> struct x36_x06<A_174> : x36_x06<A_172> {};
#endif

// Font dimension information
template <AllegroVersion version>
struct x36_x08
{
    uint32_t a, b;
    uint32_t char_height;
    uint32_t char_width;
    uint32_t xs[4];

    uint32_t TAIL;
    operator x36_x08<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};

#if __cplusplus >= 202002L
template <AllegroVersion version> requires(version >= A_172)
struct x36_x08<version>
{
    uint32_t a, b;
    uint32_t char_height;
    uint32_t char_width;
    COND_FIELD(version >= A_174, uint32_t, un2);
    uint32_t xs[4];
    uint32_t ys[8];

    uint32_t TAIL;
    operator x36_x08<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};
#else
template <> struct x36_x08<A_172>
{
    uint32_t a, b;
    uint32_t char_height;
    uint32_t char_width;
    uint32_t xs[4];
    uint32_t ys[8];

    uint32_t TAIL;
    operator x36_x08<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};

template <> struct x36_x08<A_174>
{
    uint32_t a, b;
    uint32_t char_height;
    uint32_t char_width;
    uint32_t un2;
    uint32_t xs[4];
    uint32_t ys[8];

    uint32_t TAIL;
    operator x36_x08<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};
#endif

template <AllegroVersion version>
struct x36_x0F
{
    uint32_t k;

    // Point to `x2C` instances
    uint32_t ptrs[3];

    // Points to `x03` instance
    uint32_t ptr2;
};

template <AllegroVersion version>
struct x36
{
    uint16_t t;
    uint16_t c;
    uint32_t k;
    uint32_t next;
    uint32_t size;
    
    uint32_t count;
    uint32_t last_idx;
    uint32_t un3;
    
    uint32_t TAIL;
    
    std::vector<x36_x08<version>> x08s;
    std::vector<x36_x0F<version>> x0Fs;
    
    operator x36<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};

#if __cplusplus >= 202002L
template <AllegroVersion version> requires(version >= A_172)
struct x36<version>
{
    uint16_t t;
    uint16_t c;
    uint32_t k;
    uint32_t next;
    uint32_t un1;
    uint32_t size;

    uint32_t count;
    uint32_t last_idx;
    uint32_t un3;

    COND_FIELD(version >= A_174, uint32_t, un2);

    uint32_t TAIL;

    std::vector<x36_x08<version>> x08s;
    std::vector<x36_x0F<version>> x0Fs;

    operator x36<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};
#else
template <> struct x36<A_172>
{
    uint16_t t;
    uint16_t c;
    uint32_t k;
    uint32_t next;
    uint32_t un1;
    uint32_t size;

    uint32_t count;
    uint32_t last_idx;
    uint32_t un3;

    uint32_t TAIL;

    std::vector<x36_x08<A_172>> x08s;
    std::vector<x36_x0F<A_172>> x0Fs;

    operator x36<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};

template <> struct x36<A_174>
{
    uint16_t t;
    uint16_t c;
    uint32_t k;
    uint32_t next;
    uint32_t un1;
    uint32_t size;

    uint32_t count;
    uint32_t last_idx;
    uint32_t un3;

    uint32_t un2;

    uint32_t TAIL;

    std::vector<x36_x08<A_174>> x08s;
    std::vector<x36_x0F<A_174>> x0Fs;

    operator x36<A_174>() const;
    static constexpr AllegroVersion versions[2] = {A_172, A_174};
};
#endif

#endif // X36_H
